package blasthouse.characters;

import java.util.HashMap;
import java.util.Random;
import java.util.Vector;
import java.util.logging.Logger;

import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.SpriteSheet;
import org.newdawn.slick.geom.Rectangle;
import org.newdawn.slick.geom.Vector2f;
import org.newdawn.slick.state.StateBasedGame;

import blasthouse.effects.Ability;
import blasthouse.objects.UpdatingGameElement;
import blasthouse.states.GamePlayState;
import blasthouse.states.Toolbox;

public class Eggman extends Mob {
	private static SpriteSheet eggSprite;
	private static int number = 0;
	private int timer = 0, jumptimer = 0, deathtimer = 1000;
	private float distance = 0f;
	private Random rand = new Random();
	private Vector2f directionOfTarget = new Vector2f(0,0);	
	private Vector<Ability> Abilities = new Vector<Ability>();
	private HashMap<String, Float> mobStats = new HashMap<String, Float>();

	static{
		try {
			eggSprite = new SpriteSheet(new Image("blasthouse/images/FantasticDizzy.png"), 50, 58);
			Logger.getLogger("reporter").info("Created an eggman and found the sprites");
		} catch (SlickException e) {
			e.printStackTrace();
		}		
	}
	
	public Eggman(int x, int y){
		Eggman.number++;
		this.setName("Eggman_" + Eggman.number);
		
		this.setSpriteSheet(eggSprite);
		
		this.Abilities.add(new Ability("eggmanidle", 0, 0, 5, 200, false));
		this.Abilities.add(new Ability("eggmanstun", 0, 6, 8));
		this.Abilities.add(new Ability("eggmanswing", 0, 10, 12));
		this.Abilities.add(new Ability("eggmanwalk", 1, 0, 5, 100, false));
		this.Abilities.add(new Ability("eggmanroll", 1, 6, 12, 100));
		this.Abilities.add(new Ability("eggmanfaceroll", 2, 0, 5));
		this.Abilities.add(new Ability("eggmandie", 3, 0, 3, 200, true, false));
		this.Abilities.add(new Ability("eggmanblock", 3, 4, 8));
		this.Abilities.add(new Ability("eggmanfall", 3, 9, 10));
		
		this.mobStats.put("jumpstrength", -7f);
		this.mobStats.put("speed", 2f + rand.nextFloat()*0.5f);
		this.mobStats.put("topspeed", 4.5f + rand.nextFloat()*0.5f);
				
		this.setSpriteLocX(x);
		this.setSpriteLocY(y);
	}
	
	@Override
	public void render(GameContainer gc, StateBasedGame sbg, Graphics g) throws SlickException {
		this.getSpriteSheet().getSprite(this.getCurrSpriteCol(), this.getCurrSpriteRow())
			.getFlippedCopy(this.isSpriteHFlip(), false)
			.draw(this.getSpriteLocX(), this.getSpriteLocY());
	}

	@Override
	public void update(GameContainer container, StateBasedGame game, int delta) throws SlickException {
		timer += delta;
		
		this.setBounds(new Rectangle(this.getSpriteLocX(), this.getSpriteLocY(), 50, 58));
		if( this.getCurrentAction() == null ){ setAbility("eggmanidle", true); }
		animate();
		
		if( this.isOnScreen() ){ this.setActivated(true); }
		if( !this.isOnScreen() ){ this.setActivated(false); }
		
		if( this.isActivated() ){					
			jumptimer -= delta;							
			
			if( this.isAlive() ){ think(); }
			else{ deathtimer -= delta;}
			
			move();
		}else{ 
			fall(); 
		}
		
		if( deathtimer <= 0 ){ cleanUp(); }
	}
	
	public void animate(){
		// Handles the incrementing of frames
		if( timer >= getCurrentAction().getSpeed() ){
			this.setCurrSpriteCol(this.getCurrSpriteCol() + 1);
			// Makes sure we loop or adjust incase the row changed, keeps us in line
			if( (this.getCurrSpriteCol() > getCurrentAction().getAnimColStop() ||
					this.getCurrSpriteCol() < getCurrentAction().getAnimColStart()) && 
						getCurrentAction().isRepeating() ){ 
				this.setCurrSpriteCol(getCurrentAction().getAnimColStart()); 				
			}else{
				this.setCurrSpriteCol(getCurrentAction().getAnimColStop());
			}
			// Reset timer for animation (don't subtract! just reset, subtract allows lag build up to "catchup")
			timer = 0;
		}		

		// Change the animation "row" based on the current action		
		this.setCurrSpriteRow( getCurrentAction().getAnimationRow() );				
	}
	
	public void think(){		
		// Figure out vector to get to the target		
		this.directionOfTarget = new Vector2f(this.nearestTarget().getBounds().getCenterX(), this.nearestTarget().getBounds().getCenterY()).sub(new Vector2f(this.getSpriteLocX(), this.getSpriteLocY()));		
		
		// 	Move left or right depending on relativity to target
		
			if( directionOfTarget.x > 0 && this.getMoveDirection().x < this.mobStats.get("topspeed") ){
				this.getMoveDirection().add( new Vector2f( this.mobStats.get("speed"), 0 ) );
			}
			if( directionOfTarget.x <= 0 && this.getMoveDirection().x > -this.mobStats.get("topspeed") ){
				this.getMoveDirection().add( new Vector2f( -this.mobStats.get("speed"), 0 ) );
			}
		
			//this.getMoveDirection().add( new Vector2f( (this.getMoveDirection().x > 0 ? this.mobStats.get("speed") : -this.mobStats.get("speed")), 0 ) );
		
		
		// Turns the mob to always face the target
		this.setSpriteHFlip(directionOfTarget.x > 0 ? true : false);
		
		// Sets the roll-jump ability if the target is above the mob		
		if( directionOfTarget.y < -70 || jumptimer <= 0){ 
			this.setAbility("eggmanroll", true);
			jumptimer = 200 + rand.nextInt(600);
		}
	}
	
	public void move(){
		// Apply friction
		this.getMoveDirection().add( new Vector2f(this.getMoveDirection().x > 0 ? -GamePlayState.FRICTION : (this.getMoveDirection().x < 0 ? GamePlayState.FRICTION : 0), 0f ) );	
		// Apply gravity if not on the ground
		if( !this.isOnGround() ){ this.getMoveDirection().add( GamePlayState.GRAVITY ); }
		else{ 
			this.getMoveDirection().y = 0;
			// Checks if mob is alive and wanting to jump
			if( this.checkAbility("eggmanroll") && this.isAlive() ){ 
				this.getMoveDirection().set(getMoveDirection().x, this.mobStats.get("jumpstrength")+rand.nextFloat()*0.5f );
			} 
		}
		
		// Restricts based on topspeed		
		//this.getMoveDirection().x = (this.isSpriteHFlip() ? this.mobStats.get("topspeed") : -this.mobStats.get("topspeed"));
		
		
		// Only allows the sprite to move if ALIVE, but still falls
		if( this.isAlive() ){ this.setSpriteLocX(this.getSpriteLocX() + this.getMoveDirection().x); }
		// Apply vertical motion
		this.setSpriteLocY(this.getSpriteLocY() + this.getMoveDirection().y);
		
		
		// Last check for animations before rendering
		if( this.getMoveDirection().x != 0 ){ setAbility("eggmanwalk", true); }
		if( !this.isAlive() ){ setAbility("eggmandie", true); }
	}
	
	public void fall(){
		if( !this.isOnGround() ){ this.getMoveDirection().add( GamePlayState.GRAVITY ); }
		else{ this.setMoveDirection( new Vector2f( this.getMoveDirection().x, 0 ) ); }
		// Only allows the sprite to move if dead, but still falls
		//if( this.isAlive() ){ this.setSpriteLocX(this.getSpriteLocX() + this.getMoveDirection().x); }
		// Restrict to TERMINAL VELOCITY
		this.setMoveDirection( new Vector2f( this.getMoveDirection().x, 
				this.getMoveDirection().y > GamePlayState.TERMINAL_VELOCITY 
				? GamePlayState.TERMINAL_VELOCITY : this.getMoveDirection().y ) );
		// Apply vertical motion
		this.setSpriteLocY(this.getSpriteLocY() + this.getMoveDirection().y);
	}
	
	public Ability getCurrentAction(){
		for(Ability a : this.Abilities ){
			if( a.isActive() ){ return a; }
		}
		return null;
	}
	
	public boolean checkAbility(String name){
    	// Goes through all abilities the creature has
		for( Ability ability : Abilities ){
    		// Finds the names ability
			if(ability.getName().equalsIgnoreCase(name)){ 
				return ability.isActive();
    		}
    	}	
		return false;
	}
	
	public void setAbility(String abilityName, boolean on){
    	// Goes through all abilities the creature has
		for( Ability ability : Abilities ){
    		// Finds the names ability
			if(ability.getName().equalsIgnoreCase(abilityName)){ 
				// Only resets animation if we turned on a fresh ability
				if( on && !ability.isActive() ){ 
					this.setCurrSpriteCol(ability.getAnimColStart());
					this.setCurrSpriteRow(ability.getAnimationRow());
				}
				// Sets our ability
				ability.setActive( on );				
    		}else{
    			if( on ){ ability.setActive(false); }
    		}
    	}
    }
	
	public boolean isAvailable(){
		if( !this.isAlive() ){ return false; }
    	boolean Available = true;
    	for(Ability a : Abilities){
    		if( a.isActive() && a.isOccupying() ){
    			Available = false;
    			break;
    		}
    	}    	
    	return Available;
	}
	
	public Player nearestTarget(){
		// Should set equal to player 1
		Player temp = (Player)GamePlayState.Actors.get(0);
		distance = Toolbox.distanceBetween(temp.getSpritePosition(), this.getSpritePosition());
		for( UpdatingGameElement t : GamePlayState.Actors ){
			if( t instanceof Player){
				if( Toolbox.distanceBetween(((Player)t).getSpritePosition(), this.getSpritePosition()) < distance ){
					temp = (Player)t;
					distance = Toolbox.distanceBetween(((Player)t).getSpritePosition(), this.getSpritePosition());
				}
			}
		}
		return temp;
	}
	
	public void cleanUp(){
		for( UpdatingGameElement e : GamePlayState.Actors ){
			if( e instanceof Eggman ){
				if( ((Eggman)e).equals(this)){					
					rand = null;
					directionOfTarget = null;
					mobStats = null;
					Abilities = null;
					this.setMoveDirection(null);
					this.setBounds(null);
					this.setName(null);					
					break;
				}
			}
		}
		setMarkForDelete(true);
	}
}
